package ms.unicorn.reasoning.handlers;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ms.unicorn.Element;
import ms.unicorn.Relation;
import ms.unicorn.impl.DiagramImpl;
import ms.unicorn.reasoning.UnicornDSL;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.draw2d.IFigure;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;

public class EvolutionViewBaseHandler extends AbstractHandler {

	public static final int NORMAL_VIEW = 0;
	public static final int EVOLUTION_VIEW = 1;
	public static final int ORIGIN_VIEW = 2;
	
	public static final int VISIBLE_TRUE = 1;
	public static final int VISIBLE_FALSE = 0;
	public static final int VISIBLE_PARTIAL = 2;
	
	protected int selectedView = NORMAL_VIEW;
	private Map<Element, Integer> visibilities = new HashMap<Element, Integer>();
	
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		ISelection selection = HandlerUtil.getCurrentSelection(event);
		
		if (selection instanceof StructuredSelection) {
			Object selObject = ((StructuredSelection) selection).getFirstElement();
			while (!(selObject instanceof DiagramEditPart) && selObject != null) selObject =  ((EditPart) selObject).getParent();
			DiagramEditPart editpart = (DiagramEditPart) selObject;
			visibilities.clear();
			if (editpart != null)
				updateVisibility(editpart);
		}
		return null;
	}
	
	protected void updateVisibility(GraphicalEditPart editpart) {
		View view = (View) editpart.getModel();
		Element element = (Element) view.getElement();
		if (element != null) {
			if (!(element instanceof DiagramImpl)) {
				boolean visible = getVisibility(element) != VISIBLE_FALSE;
				editpart.getFigure().setVisible(visible);
				
				for (Object ep: editpart.getSourceConnections())
					if (ep instanceof GraphicalEditPart) 
						updateVisibility((GraphicalEditPart) ep);
				
				for (Object ep: editpart.getTargetConnections())
					if (ep instanceof GraphicalEditPart) 
						updateVisibility((GraphicalEditPart) ep);
									
			}
		
			List children = editpart.getChildren();
			if (children.size() > 0)
				for (Object ed: children)
					if (ed instanceof GraphicalEditPart)
						updateVisibility((GraphicalEditPart) ed);
		}
	}
	
	public int getVisibility(Element element) {
		if (element == null)
			return VISIBLE_FALSE;
		
		int visible = VISIBLE_TRUE;
		if (!visibilities.containsKey(element)) {
			if (element instanceof Relation) {
				Relation rel = (Relation) element;
				if (getVisibility(rel.getSource()) == VISIBLE_FALSE || getVisibility(rel.getTarget()) == VISIBLE_FALSE)
					visible = VISIBLE_FALSE;
			}
			else {
				boolean originElement = UnicornDSL.ORIGIN_MODEL_ELEMENT.getValue(element);
		
				switch(selectedView) {
				case EVOLUTION_VIEW:
					// true is element is an observable or not an original element
					visible = (!originElement ||  UnicornDSL.OBSERVABLE.equals(element.getElementType())) ? VISIBLE_TRUE: VISIBLE_FALSE ;
					// otherwise, true, if element connect to an observable either incoming
					if (visible != VISIBLE_TRUE) 
						for (Relation rel: element.getIncoming())
							if (rel.getSource() != null) // might be null in the case DELETE didn't do well
								if (UnicornDSL.OBSERVABLE.equals(rel.getSource().getElementType())) visible = VISIBLE_TRUE;
								else if (UnicornDSL.REFINES.equals(rel.getElementType()) && getVisibility(rel.getSource()) == VISIBLE_TRUE)
									visible = VISIBLE_TRUE;
					if (visible != VISIBLE_TRUE)
						for (Relation rel: element.getOutgoing())
							if (rel.getTarget() != null && UnicornDSL.OBSERVABLE.equals(rel.getTarget().getElementType())) visible = VISIBLE_PARTIAL;
					break;
				case ORIGIN_VIEW:
					visible = originElement && !UnicornDSL.OBSERVABLE.equals(element.getElementType()) ? VISIBLE_TRUE: VISIBLE_FALSE;
					break;
				}
			}
			visibilities.put(element, visible);
			System.out.printf("Element %s (%s) -> %d\n", element.getName(), element.getElementType(), visible);
		}
		else visible = visibilities.get(element);
		return visible;
	}

}
